package com.zsc.wxm.ysh.anjo.es.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.zsc.wxm.ysh.anjo.es.config.SearchFiled;
import com.zsc.wxm.ysh.anjo.es.service.SearchService;
import com.zsc.wxm.ysh.anjo.result.PageResult;
import com.zsc.wxm.ysh.anjo.service.base.constant.EsConstant;
import com.zsc.wxm.ysh.anjo.service.base.model.es.qo.SearchQuery;
import com.zsc.wxm.ysh.anjo.service.base.model.es.vo.EsDataVO;
import com.zsc.wxm.ysh.anjo.utils.ExceptionUtil;
import com.zsc.wxm.ysh.anjo.utils.MyDateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 描述：
 *
 * @author playmaker
 * @since 2021/2/15 19:41
 */
@Service
@Slf4j
public class SearchServiceImpl implements SearchService {

    @Resource
    private RestHighLevelClient client;

    @Override
    public PageResult searchHouseResources(SearchQuery searchQuery) {
        //检索请求
        SearchRequest searchRequest = new SearchRequest(EsConstant.HOUSE_INDEX);

        //根据参数构建检索器
        SearchSourceBuilder searchSourceBuilder = searchRequestBuilder(searchQuery);

        searchRequest.source(searchSourceBuilder);

        SearchResponse searchResponse = null;

        try {
            searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            return searchResult(searchResponse, searchQuery);
        } catch (IOException e) {
            log.error(ExceptionUtil.getMessage(e));
        }
        return null;
    }

    @Override
    public void deleteIndexDataByBatch(List<String> ids) throws IOException {
        if (CollUtil.isEmpty(ids)) {
            return;
        }
        BulkRequest bulkRequest = new BulkRequest();
        for (String id : ids) {
            DeleteRequest deleteRequest = new DeleteRequest(EsConstant.HOUSE_INDEX, id);
            bulkRequest.add(deleteRequest);
        }
        client.bulk(bulkRequest, RequestOptions.DEFAULT);

    }

    @Override
    public PageResult homeRecommend(String province, String city, Integer currPage) throws ParseException {
        SearchRequest searchRequest = new SearchRequest(EsConstant.HOUSE_INDEX);
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //省
        if (StringUtils.isNotBlank(province)) {
            //条件设置，省份优先最高,bootst = 千万级
            boolQueryBuilder.must(QueryBuilders.matchQuery(SearchFiled.PROVINCE, province).boost(10000000F));
        }
        //城市 百万级
        if (StringUtils.isNotBlank(city)) {
            boolQueryBuilder.must(QueryBuilders.matchQuery(SearchFiled.CITY, city).boost(9000000F));
        }

        //条件设置，更新时间 百万级
        Date now = new Date();
        Date beginData = MyDateUtil.getBeginDate(now);
        Date overDate = MyDateUtil.getOverDate(now);
        //今天
        boolQueryBuilder.should(QueryBuilders.rangeQuery(SearchFiled.MODIFY_TIME)
                .from(DateUtil.offsetDay(beginData, -100).toJdkDate())
                .to(overDate)
                .boost(8000000F));
        //前天
        boolQueryBuilder.should(QueryBuilders.rangeQuery(SearchFiled.MODIFY_TIME)
                .from(DateUtil.offsetDay(beginData, -200).toJdkDate())
                .to(DateUtil.offsetDay(overDate, -200).toJdkDate())
                .boost(7000000F));
        //大前天
        boolQueryBuilder.should(QueryBuilders.rangeQuery(SearchFiled.MODIFY_TIME)
                .from(DateUtil.offsetDay(beginData, -300).toJdkDate())
                .to(DateUtil.offsetDay(overDate, -300).toJdkDate())
                .boost(6000000F));
        //大大前天
        boolQueryBuilder.should(QueryBuilders.rangeQuery(SearchFiled.MODIFY_TIME)
                .from(DateUtil.offsetDay(beginData, -40).toJdkDate())
                .to(DateUtil.offsetDay(overDate, -40).toJdkDate())
                .boost(5000000F));

        //大大大前天
        boolQueryBuilder.should(QueryBuilders.rangeQuery(SearchFiled.MODIFY_TIME)
                .from(DateUtil.offsetDay(beginData, -500).toJdkDate())
                .to(DateUtil.offsetDay(overDate, -500).toJdkDate())
                .boost(4000000F));
        //大大大大前天
        boolQueryBuilder.should(QueryBuilders.rangeQuery(SearchFiled.MODIFY_TIME)
                .from(DateUtil.offsetDay(beginData, -180).toJdkDate())
                .to(DateUtil.offsetDay(overDate, -180).toJdkDate())
                .boost(3000000F));


        SearchSourceBuilder builder = new SearchSourceBuilder();
        builder.query(boolQueryBuilder);
        //租金升序排序
        builder.sort(SortBuilders.fieldSort(SearchFiled.RENT).order(SortOrder.DESC));
        //分页
        builder.from(((currPage == null ? 1 : currPage)  - 1) * SearchFiled.PAGE_SIZE);
        builder.size(SearchFiled.PAGE_SIZE);
        searchRequest.source(builder);

        SearchResponse searchResponse = null;

        try {
            searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            long total = searchResponse.getHits().getTotalHits().value;
            List<EsDataVO> data = new ArrayList<>();
            if (total > 0) {
                for (SearchHit hit : searchResponse.getHits().getHits()) {
                    String source = hit.getSourceAsString();
                    EsDataVO vo = JSON.parseObject(source, EsDataVO.class);
                    data.add(vo);
                }
            }
            return PageResult.ok(data, (int) total);
        } catch (IOException e) {
            log.error(ExceptionUtil.getMessage(e));
        }
        return null;
    }


    /**
     * 构建一个检索资源构建器
     * @param query
     * @return
     */
    private SearchSourceBuilder searchRequestBuilder(SearchQuery query) {
        log.info("查询参数：{}", query);

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        //省份
        if (StringUtils.isNotBlank(query.getProvince())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery(SearchFiled.PROVINCE, query.getProvince()));
        }

        //城市
        if (StringUtils.isNotBlank(query.getCity())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery(SearchFiled.CITY, query.getCity()));
        }

        //价格区间查询
        if (StringUtils.isNotBlank(query.getRent())) {
            RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(SearchFiled.RENT);
            String[] rents = query.getRent().split("-");
            rangeQueryBuilder.gte(rents[0]).lte(rents[1]);
            boolQueryBuilder.filter(rangeQueryBuilder);
        }

        //标题
        if (StringUtils.isNotBlank(query.getTitle())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery(SearchFiled.TITLE, query.getTitle()));
        }

        //地址
        if (StringUtils.isNotBlank(query.getAddress())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery(SearchFiled.ADDRESS, query.getAddress()));
        }

        //组装到搜索资源构建器
        SearchSourceBuilder builder = new SearchSourceBuilder();
        builder.query(boolQueryBuilder);

        //分页
        builder.from(((query.getCurrPage() == null ? 1 : query.getCurrPage())  - 1) * SearchFiled.PAGE_SIZE);
        builder.size(SearchFiled.PAGE_SIZE);

        //高亮
        if (StringUtils.isNotBlank(query.getTitle())) {
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field(SearchFiled.TITLE);
            highlightBuilder.preTags("<em style='color:red'>");
            highlightBuilder.postTags("</em>");
            builder.highlighter(highlightBuilder);
        }

        //地址
        if (StringUtils.isNotBlank(query.getAddress())) {
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field(SearchFiled.ADDRESS);
            highlightBuilder.preTags("<em style='color:red'>");
            highlightBuilder.postTags("</em>");
            builder.highlighter(highlightBuilder);
        }

        return builder;

    }

    /**
     * 对检索结果进行封装、处理
     *
     * @param searchResponse
     * @param query
     * @return
     */
    private PageResult searchResult(SearchResponse searchResponse, SearchQuery query) {
        long total = searchResponse.getHits().getTotalHits().value;
        List<EsDataVO> data = new ArrayList<>();
        if (total > 0) {
            for (SearchHit hit : searchResponse.getHits().getHits()) {
                EsDataVO vo = getEsDataVO(query, hit);
                data.add(vo);
            }
        }
        return PageResult.ok(data, (int) total);
    }

    private EsDataVO getEsDataVO(SearchQuery query, SearchHit hit) {
        String source = hit.getSourceAsString();
        EsDataVO vo = JSON.parseObject(source, EsDataVO.class);
        if (StringUtils.isNotBlank(query.getTitle()) || StringUtils.isNotBlank(query.getAddress())) {
            for (Map.Entry<String, HighlightField> entry : hit.getHighlightFields().entrySet()) {
                String key = entry.getKey();
                String value = entry.getValue().getFragments()[0].toString();
                if (StringUtils.isNotBlank(query.getTitle()) && SearchFiled.TITLE.equals(key)) {
                    vo.setTitle(value);
                }
                if (StringUtils.isNotBlank(query.getAddress()) && SearchFiled.ADDRESS.equals(key)) {
                    vo.setAddress(value);
                }
            }
        }
        return vo;
    }


}
